﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mendel
{
    public class Processor
    {
        public RMode RunMode { get; set; }

        public LinkedListStack<Item> DataStack { get; set; }

        public LinkedListStack<Item> ReturnStack { get; private set; }

        public LinkedListStack<Item> ControlStack { get; private set; }

        private string output;

        public string Output { get; internal set; }

        public Processor()
        {
            RunMode = RMode.Normal;
            output = "";
            DataStack = new LinkedListStack<Item>();
            ReturnStack = new LinkedListStack<Item>();
            ControlStack = new LinkedListStack<Item>();
        }

        private Item Compile(Item tobecompiled)
        {
            if (tobecompiled.Constant && tobecompiled.Type == DType.String)            // csak ilyeneket lehet lefordítani
            {
                string s = tobecompiled.ST;

                bool reference = false;
                if (s[0] == '@')
                {
                    reference = true;
                    s = s.Substring(1);
                }

                try
                {
                    int i = System.Convert.ToInt32(s);
                    return new Item(new Integer(i));        // egész szám konstans
                }
                catch (Exception)
                { }

                try
                {
                    double d = System.Convert.ToDouble(s);
                    return new Item(new Float(d));          // valós szám konstans
                }
                catch (Exception)
                { }

                Item tmp;
                tmp = Universe.GetByName(s);                        // megnézzük, hogy benne van-e a nyilvántartásban

                if (tmp != null)
                {
                    if (reference)
                        return new Item(new Reference(tmp));
                    else
                        return tmp;
                }
                else
                {
                    return new Item(new String(s));                 // sztring konstans
                }
            }
            else
            {
                return tobecompiled;
            }
        }

        private Item DeCompile(Item tobedecompiled)
        {
            if (tobedecompiled.Constant)
            {
                return new Item(new String(tobedecompiled.ST));
            }
            else
            {
                Universe.RemoveItem(tobedecompiled);
                return new Item(new String(tobedecompiled.Name));
            }
        }

        public Item Execute(Item toberan)
        {
            try
            {
                Item tmp = Compile(toberan);

                if (tmp.Type == DType.Word)
                {
                    if (!tmp.WO.Execute(this))
                        ReturnStack.Push(tmp);
                }
                else if (RunMode == RMode.Normal)
                {
                    if (tmp.Type == DType.Reference)
                    {
                        DataStack.Push(tmp.RE);
                    }
                    else
                    {
                        DataStack.Push(tmp);
                    }
                }
                else if (RunMode == RMode.Compile)
                {
                    DataStack.Push(tmp);
                }

                return tmp;
            }
            catch (Exception e)
            {
                Universe.AddError(e);
                return toberan;
            }
        }

        // timeout: -1: végtelenségig vár, 0: nem vár, >0: timeouttal vár
        public void Run(Word input, int timeout=0)
        {
            if (input == null)
                return;

            bool[] tmp = new bool[] { input.runmodes[0], input.runmodes[1], input.runmodes[2] };

            input.runmodes[0] = input.runmodes[1] = input.runmodes[2] = true;

            ReturnStack.Push(input.Container);

            var task = new Task(() =>
                {
                    while (ReturnStack.Count > 0)
                    {
                        if (ReturnStack.Last().WO.Execute(this))
                            ReturnStack.RemoveLast();
                    }
                });
            task.ContinueWith((antecedent) =>
                {
                    input.runmodes[0] = tmp[0];
                    input.runmodes[1] = tmp[1];
                    input.runmodes[2] = tmp[2];
                });

            task.Start();

            if (timeout == -1)
                task.Wait();
            else if (timeout > 0)
                task.Wait(timeout);


        }

        public void Run(string input, int timeout=0)
        {
            Run((Word)(new String(input).Convert(DType.Word)), timeout);
        }

        public void ClearOutput()
        {
            Output = "";
        }
    }

    
}
